﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SMA = System.Management.Automation;

namespace RSPowershell
{
    [System.Management.Automation.Cmdlet("Print", "RSReport")]
    public class Print_RSReport : SMA.Cmdlet
    {

        [SMA.Parameter(Mandatory = true, Position = 0)]
        public string Path;

        [SMA.Parameter(Mandatory = false)]
        public DeviceInfo DeviceInfo;

        [SMA.Parameter(Mandatory = false)]
        public string PaperSource;

        [SMA.Parameter(Mandatory = true)]
        public PrinterPreferences PrinterPreferences;

        [SMA.Parameter(Mandatory = false)]
        public int FirstNPages=-1;

        private List<System.Drawing.Imaging.Metafile> metafiles;
                
        protected override void ProcessRecord()
        {
            GetRenderedMetafiles();
            PrintMetafiles();
        }

        private void GetRenderedMetafiles()
        {
            var rep_svc = this.ConnectToReportingService();
            var rep_exec_svc = this.ConnectToReportExecutionService();

            var exec_info = new ReportExecutionService.ExecutionInfo();
            var exec_header = new ReportExecutionService.ExecutionHeader();

            rep_exec_svc.ExecutionHeaderValue = exec_header;
            string historyid = null;

            this.WriteVerbose("Loading Report");
            exec_info = rep_exec_svc.LoadReport(this.Path, historyid);

            //rs2.SetExecutionParameters(parameters, "en-us");

            var SessionId = rep_exec_svc.ExecutionHeaderValue.ExecutionID;
            this.WriteVerbose("SessionID: {0}", rep_exec_svc.ExecutionHeaderValue.ExecutionID);

            var streams = new List<System.IO.Stream>();
            this.metafiles = new List<System.Drawing.Imaging.Metafile>();
            string render_format = "IMAGE";
            this.WriteVerbose("Render Format: {0}", render_format);

            int cur_page_count = 0;

            try
            {


                // Create DeviceInfo XML
                var devinfo = this.DeviceInfo ?? new DeviceInfo();
                devinfo.OutputFormat = "EMF"; // force it to emf
                devinfo.Toolbar = false;
                devinfo.PrintDpiX = 300;
                devinfo.PrintDpiY = 300;
                string device_info = devinfo.ToString();
                this.WriteVerbose("DeviceInfo: {0}", device_info);


                // Create the Viewer and Bind it to the Server Report
                this.WriteVerbose("Creating the ReportViewer Control");
                var viewer = new Microsoft.Reporting.WinForms.ReportViewer();
                viewer.ProcessingMode = Microsoft.Reporting.WinForms.ProcessingMode.Remote;
                var server_report = viewer.ServerReport;
                this.WriteVerbose("Server Report DisplayName: {0}", server_report.DisplayName);
                this.WriteVerbose("Server Report ReportPath: {0}", server_report.ReportPath);
                this.WriteVerbose("Server Report ReportServerUrl: {0}", server_report.ReportServerUrl);
                this.WriteVerbose("Server Report Timeout: {0}", server_report.Timeout);
                this.WriteVerbose("Server Version: {0}", server_report.GetServerVersion());


                viewer.ServerReport.ReportServerUrl = new Uri(this.get_correct_url(rep_svc.Url));
                viewer.ServerReport.ReportPath = this.Path;
                this.WriteVerbose("Report Viewer ProcessingMode: {0}", viewer.ProcessingMode);
                this.WriteVerbose("Report Viewer ReportServerUrl: {0}", viewer.ServerReport.ReportServerUrl);
                this.WriteVerbose("Report Viewer ReportPath: {0}", viewer.ServerReport.ReportPath);

                // Setup URL Parameters
                this.WriteVerbose("Creating the URL Parameters");
                var url_params = new System.Collections.Specialized.NameValueCollection();
                url_params.Add("rs:PersistStreams", "True");

                // render first stream
                this.WriteVerbose("Starting Rendering First Stream");

                string rendered_mimetype;
                string rendered_extension;

                var rendered_stream = server_report.Render(
                    render_format,
                    device_info,
                    url_params,
                    out rendered_mimetype,
                    out rendered_extension);

                this.WriteVerbose("Finished Rendering First Stream");

                streams.Add(rendered_stream);

                cur_page_count++;
                // Handle the addtional streams

                this.WriteVerbose("Retrieving Additional Streams");
                url_params.Remove("rs:PersistStreams");
                url_params.Add("rs:GetNextStream", "True");
                do
                {
                    // Check to see if user only wanted the first N pages of the report

                    if (this.FirstNPages > 0)
                    {
                        if (cur_page_count >= this.FirstNPages)
                        {
                            break;
                        }
                    }

                    // ------------
                    this.WriteVerbose("Starting Rendering Additional Stream");
                    rendered_stream = server_report.Render(
                        render_format, 
                        device_info, 
                        url_params, 
                        out rendered_mimetype, 
                        out rendered_extension);

                    if (rendered_stream.Length != 0)
                    {
                        this.WriteVerbose("Storing stream");
                        streams.Add(rendered_stream);
                    }
                    else
                    {
                        this.WriteVerbose("Received stream of length zero");
                    }

                    this.WriteVerbose("Finished Rendering Additional Stream");
                    cur_page_count++;

                } while (rendered_stream.Length > 0);
                this.WriteVerbose("Finished Retrieving Additional Streams");

            }
            catch (System.Web.Services.Protocols.SoapException err)
            {
                this.WriteVerbose("Caught SoapException");
                this.WriteVerbose("Message: {0}", err.Message);
                throw err;
            }
            finally
            {

                // Convert each stream into a metafile

                foreach (var stream in streams)
                {
                    try
                    {
                        this.WriteVerbose("Stream type: {0}", stream.GetType());
                        this.WriteVerbose("Converting to metafile");

                        var memstream = (System.IO.MemoryStream)stream;
                        this.WriteVerbose("memstream size: {0}", memstream.Length);
                        var metafile = new System.Drawing.Imaging.Metafile(memstream);
                        metafiles.Add(metafile);
                        this.WriteVerbose("metafile size: {0}", metafile.Size);
                    }
                    finally
                    {
                        // Get rid of the steams
                        stream.Close();
                        stream.Dispose();
                    }

                }

                streams = null;
            }
        }

        private void PrintMetafiles()
        {
            this.WriteVerbose("Creating new Metafile printer");
            this.WriteVerbose(" for print pref printer {0}", this.PrinterPreferences.Printer);

            var mfp = new MetafilePrinter(this, this.PrinterPreferences);

            foreach (var metafile in this.metafiles)
            {
                mfp.Metafiles.Add(metafile);
            }

            mfp.PrintMetafiles();
        }


        private string get_correct_url(string s)
        {
            var tokens = s.Split(new[] { '/' });
            var tokens2 = tokens.Take(tokens.Length - 1).ToArray();
            return string.Join("/", tokens2);
        }
    }
}